Android 9.0系统源码

您所在的位置:网站首页 android源码设计模式解析与实战 pdf Android 9.0系统源码

Android 9.0系统源码

2023-03-16 14:21| 来源: 网络整理| 查看: 265

前言

NavigationBar 和 StatusBar 都属于 SystemBar,也叫做 decor,就是说给 App 装饰的意思。一般的 window 的布局是在 PhoneWindowManager 的 layoutWindowLw() 方法中,而 SystemBar 是在 beginLayoutLw() 方法中布局。

当前最上层的 Activity 可以修改 SystemBar 的 visibility,可以调用 View#setSystemUiVisibility() 方法,系统也有一些针对 SystemBar visibility 的策略。最终的 visibility 保存在 PhoneWindowManager 中的 mLastSystemUiFlags 变量中。

一、简单认识DisplayFrames

在分析NaivgationBar和StatusBar对应的窗口布局前,需要我们先来简单认识Android中的DisplayFrames对象。

frameworks/base/services/core/java/com/android/server/wm/DisplayFrames.java

public class DisplayFrames { //物理屏幕相关的设备id public final int mDisplayId; /** * The current size of the screen; really; extends into the overscan area of the screen and * doesn't account for any system elements like the status bar. */ //当前的屏幕大小,包括过扫描区域。过扫描区域在输出到 TV 时会用到, // 对于移动设备来说 mOverscan 大小就是物理设备的大小 (0,0)-(dw,dh)。 public final Rect mOverscan = new Rect(); /** * The current visible size of the screen; really; (ir)regardless of whether the status bar can * be hidden but not extending into the overscan area. */ //当前可见的屏幕大小,其实就是 (0,0)-(dw,dh)。 public final Rect mUnrestricted = new Rect(); /** Like mOverscan*, but allowed to move into the overscan region where appropriate. */ //是应用可显示的区域,包含 StatusBar 的区域,不包含 NavigationBar 区域。 public final Rect mRestrictedOverscan = new Rect(); /** * The current size of the screen; these may be different than (0,0)-(dw,dh) if the status bar * can't be hidden; in that case it effectively carves out that area of the display from all * other windows. */ //一般情况下 mRestrictedOverscan 与 mRestricted 相同 public final Rect mRestricted = new Rect(); /** * During layout, the current screen borders accounting for any currently visible system UI * elements. */ //是布局过程中,当前画面的边界,包含 Translucent(半透明)区域。一般情况下 NavigationBar 区域不是 Translucent,而 StatusBar 是 Translucent。 public final Rect mSystem = new Rect(); /** For applications requesting stable content insets, these are them. */ //是应用窗口的显示区域,不包含 StatusBar 和 NavigationBar。 public final Rect mStable = new Rect(); /** * For applications requesting stable content insets but have also set the fullscreen window * flag, these are the stable dimensions without the status bar. */ //是当Activity设置Fullscreen flag 时候的窗口显示区域,这时 StatusBar 会隐藏。 public final Rect mStableFullscreen = new Rect(); /** * During layout, the current screen borders with all outer decoration (status bar, input method * dock) accounted for. */ // 是布局的时候除去外部装饰的窗口(例如 StatusBar 和输入法窗口)。 public final Rect mCurrent = new Rect(); /** * During layout, the frame in which content should be displayed to the user, accounting for all * screen decoration except for any space they deem as available for other content. This is * usually the same as mCurrent*, but may be larger if the screen decor has supplied content * insets. */ // 是当前应该给用户显示的窗口,通常与 mCurrent 相同。当装饰窗口提供内容插入的时候,有可能比 mCurrent 更大。 public final Rect mContent = new Rect(); /** * During layout, the frame in which voice content should be displayed to the user, accounting * for all screen decoration except for any space they deem as available for other content. */ //mVoiceContent 通常与 mContent 相同。 public final Rect mVoiceContent = new Rect(); /** During layout, the current screen borders along which input method windows are placed. */ //mDock 是输入法布局时的边界。 public final Rect mDock = new Rect(); /** The display cutout used for layout (after rotation) */ //用于刘海屏布局的剪刀工具,Android 9.0 新加入的。 @NonNull public WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT; /** The cutout as supplied by display info */ //用于刘海屏布局的剪刀工具,Android 9.0 新加入的。 @NonNull public WmDisplayCutout mDisplayInfoCutout = WmDisplayCutout.NO_CUTOUT; /** * During layout, the frame that is display-cutout safe, i.e. that does not intersect with it. */ //是在刘海屏上可以安全显示的区域,即这个区域与刘海区域没有交集。 public final Rect mDisplayCutoutSafe = new Rect(); private final Rect mDisplayInfoOverscan = new Rect(); private final Rect mRotatedDisplayInfoOverscan = new Rect(); public int mDisplayWidth;//物理屏幕宽度 public int mDisplayHeight;//物理屏幕高度 public int mRotation;//屏幕旋转角度 public DisplayFrames(int displayId, DisplayInfo info, WmDisplayCutout displayCutout) { mDisplayId = displayId; onDisplayInfoUpdated(info, displayCutout); } public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout) { mDisplayWidth = info.logicalWidth; mDisplayHeight = info.logicalHeight; mRotation = info.rotation; mDisplayInfoOverscan.set( info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom); mDisplayInfoCutout = displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT; } public void onBeginLayout() { switch (mRotation) { case ROTATION_90: mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top; mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.right; mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.bottom; mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.left; break; case ROTATION_180: mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.right; mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.bottom; mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.left; mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.top; break; case ROTATION_270: mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.bottom; mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.left; mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.top; mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.right; break; default: mRotatedDisplayInfoOverscan.set(mDisplayInfoOverscan); break; } mRestrictedOverscan.set(0, 0, mDisplayWidth, mDisplayHeight); mOverscan.set(mRestrictedOverscan); mSystem.set(mRestrictedOverscan); mUnrestricted.set(mRotatedDisplayInfoOverscan); mUnrestricted.right = mDisplayWidth - mUnrestricted.right; mUnrestricted.bottom = mDisplayHeight - mUnrestricted.bottom; mRestricted.set(mUnrestricted); mDock.set(mUnrestricted); mContent.set(mUnrestricted); mVoiceContent.set(mUnrestricted); mStable.set(mUnrestricted); mStableFullscreen.set(mUnrestricted); mCurrent.set(mUnrestricted); mDisplayCutout = mDisplayInfoCutout; mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); if (!mDisplayCutout.getDisplayCutout().isEmpty()) { final DisplayCutout c = mDisplayCutout.getDisplayCutout(); if (c.getSafeInsetLeft() > 0) { mDisplayCutoutSafe.left = mRestrictedOverscan.left + c.getSafeInsetLeft(); } if (c.getSafeInsetTop() > 0) { mDisplayCutoutSafe.top = mRestrictedOverscan.top + c.getSafeInsetTop(); } if (c.getSafeInsetRight() > 0) { mDisplayCutoutSafe.right = mRestrictedOverscan.right - c.getSafeInsetRight(); } if (c.getSafeInsetBottom() > 0) { mDisplayCutoutSafe.bottom = mRestrictedOverscan.bottom - c.getSafeInsetBottom(); } } } } 二、构建SystemBar对应的视图窗口区域坐标对象

1、系统主要是在PhoneWindowManager的beginLayoutLw() 方法中构建SystemBar对应的视图窗口区域坐标对象的。

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

public class PhoneWindowManager implements WindowManagerPolicy { @Override public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { displayFrames.onBeginLayout(); // TODO(multi-display): This doesn't seem right...Maybe only apply to default display? mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); mDockLayer = 0x10000000; mStatusBarLayer = -1; // start with the current dock rect, which will be (0,0,displayWidth,displayHeight) final Rect pf = mTmpParentFrame; final Rect df = mTmpDisplayFrame; final Rect of = mTmpOverscanFrame; final Rect vf = mTmpVisibleFrame; final Rect dcf = mTmpDecorFrame; vf.set(displayFrames.mDock); of.set(displayFrames.mDock); df.set(displayFrames.mDock); pf.set(displayFrames.mDock); dcf.setEmpty(); // Decor frame N/A for system bars. if (displayFrames.mDisplayId == DEFAULT_DISPLAY) { // For purposes of putting out fake window up to steal focus, we will // drive nav being hidden only by whether it is requested. //获取窗口systemui的标记类型 final int sysui = mLastSystemUiFlags; //navigationBar是否可见 boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; //navigationBar是否是半透明的 boolean navTranslucent = (sysui & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0; boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; //navigationBar是否允许隐藏 boolean navAllowedHidden = immersive || immersiveSticky; navTranslucent &= !immersiveSticky; // transient trumps translucent boolean isKeyguardShowing = isStatusBarKeyguard() && !mKeyguardOccluded; if (!isKeyguardShowing) { navTranslucent &= areTranslucentBarsAllowed(); } boolean statusBarExpandedNotKeyguard = !isKeyguardShowing && mStatusBar != null && mStatusBar.getAttrs().height == MATCH_PARENT && mStatusBar.getAttrs().width == MATCH_PARENT; // When the navigation bar isn't visible, we put up a fake input window to catch all // touch events. This way we can detect when the user presses anywhere to bring back the // nav bar and ensure the application doesn't see the event. if (navVisible || navAllowedHidden) { if (mInputConsumer != null) { mHandler.sendMessage( mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); mInputConsumer = null; } } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(), INPUT_CONSUMER_NAVIGATION, (channel, looper) -> new HideNavInputEventReceiver(channel, looper)); // As long as mInputConsumer is active, hover events are not dispatched to the app // and the pointer icon is likely to become stale. Hide it to avoid confusion. InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); } // For purposes of positioning and showing the nav bar, if we have decided that it can't // be hidden (because of the screen aspect ratio), then take that into account. navVisible |= !canHideNavigationBar(); //调用layoutNavigationBar构建导航栏 boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf, navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard); if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock); //调用layoutStatusBar构建状态栏 updateSysUiVisibility |= layoutStatusBar( displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing); if (updateSysUiVisibility) { updateSystemUiVisibilityLw(); } } layoutScreenDecorWindows(displayFrames, pf, df, dcf); if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { // Make sure that the zone we're avoiding for the cutout is at least as tall as the // status bar; otherwise fullscreen apps will end up cutting halfway into the status // bar. displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, displayFrames.mStable.top); } } }

beginLayoutLw方法中首先会判断NavigationBar是否可见以及是否半透明,然后调用layoutNavigationBar方法和layoutStatusBar方法构建NavigationBar和StatusBar所对应的视图窗口区域坐标。

三、导航栏占位视图窗口区域坐标对象的构建

构建导航栏视图窗口区域坐标的layoutNavigationBar方法r如下所示。

public class PhoneWindowManager implements WindowManagerPolicy { //导航栏视图对应的窗口状态 WindowState mNavigationBar = null; //导航栏视图对应的窗口区域坐标 static final Rect mTmpNavigationFrame = new Rect(); //导航栏视图控制器 private final BarController mNavigationBarController = new BarController("NavigationBar", View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_UNHIDE, View.NAVIGATION_BAR_TRANSLUCENT, StatusBarManager.WINDOW_NAVIGATION_BAR, FLAG_TRANSLUCENT_NAVIGATION, View.NAVIGATION_BAR_TRANSPARENT); //构建导航栏视图对应的窗口区域坐标 private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf, boolean navVisible, boolean navTranslucent, boolean navAllowedHidden, boolean statusBarExpandedNotKeyguard) { if (mNavigationBar == null) { return false; } boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); //根据屏幕旋转角度,我们需要为导航栏视图设置相对应的位置和大小 final int rotation = displayFrames.mRotation; final int displayHeight = displayFrames.mDisplayHeight; final int displayWidth = displayFrames.mDisplayWidth; final Rect dockFrame = displayFrames.mDock; //获取导航栏的位置 mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); //cutoutSafeUnrestricted是安全的窗口(Android9针对刘海平新增的),当没有Overscan的时候与mUnrestricted相同 //即cutoutSafeUnrestricted.bottom的值与DisplayFrames.mDisplayHeight值相同。 final Rect cutoutSafeUnrestricted = mTmpRect; cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); //导航栏在底部 if (mNavigationBarPosition == NAV_BAR_BOTTOM) { //计算导航栏的左上角的Y坐标 final int top = cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode); //mTmpNavigationFrame就是NavigationBar所对应的窗口区域。 mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom); //mStable对应应用窗口的显示区域,mStableFullscreen对应应用窗口全屏的显示区域,这里的设置使得应用窗口正常状态和全屏的时候都在导航栏的上方 displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); } else if (navVisible) { //如果NavigationBar可见的话,更新dockFrame、mRestricted、mRestrictedOverscan的bottom值 mNavigationBarController.setBarShowingLw(true); dockFrame.bottom = displayFrames.mRestricted.bottom = displayFrames.mRestrictedOverscan.bottom = top; } else { // We currently want to hide the navigation UI - unless we expanded the status bar. mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); } if (navVisible && !navTranslucent && !navAllowedHidden && !mNavigationBar.isAnimatingLw() && !mNavigationBarController.wasRecentlyTranslucent()) { // If the opaque nav bar is currently requested to be visible and not in the process // of animating on or off, then we can tell the app that it is covered by it. displayFrames.mSystem.bottom = top; } } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { ...代码省略... } else if (mNavigationBarPosition == NAV_BAR_LEFT) { ...代码省略... } //使用dockFrame的参数去更新mCurrent,mVoiceContent,mContent displayFrames.mCurrent.set(dockFrame); displayFrames.mVoiceContent.set(dockFrame); displayFrames.mContent.set(dockFrame); mStatusBarLayer = mNavigationBar.getSurfaceLayer(); //计算NavigationBar视图所对应的窗口坐标对应contentFrame的大小 mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf, mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */); //将NavigationBar的contentFrame给到mNavigationBarController。 mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw()); if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame); return mNavigationBarController.checkHiddenLw(); } //获取导航栏的位置 private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { if (mNavigationBarCanMove && displayWidth > displayHeight) { if (displayRotation == Surface.ROTATION_270) { return NAV_BAR_LEFT; } else { return NAV_BAR_RIGHT; } } return NAV_BAR_BOTTOM; } }

对以上代码做个简单总结:

1)layoutNavigationBar方法首先会调用 navigationBarPosition()方法返回 NavigationBar 的位置,根据返回的位置的不同所执行的布局方式也会不同。

2)我们这里以布局在下方为例,计算导航栏NavigationBar的左上角Y坐标top,top的值等于cutoutSafeUnrestricted.bottom减去NavigationBar高度。cutoutSafeUnrestricted是安全的窗口(Android 9 针对刘海平新增的),当没有 Overscan 的时候与 mUnrestricted 相同,即 cutoutSafeUnrestricted.bottom 的值与 DisplayFrames.mDisplayHeight 值相同。

3)在得到导航栏NavigationBar的左上角Y坐标top之后,会将导航栏相关的坐标给到mTmpNavigationFrame。mTmpNavigationFrame就是导航栏NavigationBar视图所对应的窗口区域。之后会更新应用程序安全显示区域mStable和应用程序全屏安全显示区域mStableFullscreen的最下方Y坐标bottom值为top。这就是为什么NavigationBar视图占用应用显示区域的原因。

4)如果 NavigationBar 可见的话则更新 dockFrame、mRestricted、mRestrictedOverscan 的 bottom 值。然后用 dockFrame 去更新 mCurrent、mVoiceContent、mContent。最后调用WindowState的computeFrameLw() 方法计算NavigationBar视图所对应的窗口坐标contentFrame的大小,并将结果给到mNavigationBarController对象。

四、状态栏视图窗口区域坐标对象的构建

1、构建状态栏视图窗口区域坐标的layoutStatusBar方法r如下所示。

public class PhoneWindowManager implements WindowManagerPolicy { //状态栏视图所对应的窗口状态 WindowState mStatusBar = null; private static final Rect mTmpRect = new Rect(); private final StatusBarController mStatusBarController = new StatusBarController(); //构建状态栏视图对应的窗口区域坐标 private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui, boolean isKeyguardShowing) { // decide where the status bar goes ahead of time if (mStatusBar == null) { return false; } // apply any navigation bar insets of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); vf.set(displayFrames.mStable); mStatusBarLayer = mStatusBar.getSurfaceLayer(); //计算StatusBar的mContentFrame的大小 mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */, dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */, displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */); //因为StatusBar默认显示在顶部的,所以修改应用程序安全显示区域mStable.top的值。 displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeightForRotation[displayFrames.mRotation]; // Make sure the status bar covers the entire cutout height displayFrames.mStable.top = Math.max(displayFrames.mStable.top, displayFrames.mDisplayCutoutSafe.top); //计算 StatusBar 的可显示区域,并设置给mStatusBarController。 mTmpRect.set(mStatusBar.getContentFrameLw()); mTmpRect.intersect(displayFrames.mDisplayCutoutSafe); mTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset mTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size mStatusBarController.setContentFrame(mTmpRect); //判断 StatusBar 是否是短暂显示的(Transient)或是半透明的(Translucent)。其中 sysui 就是 mLastSystemUiFlags。 boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; boolean statusBarTranslucent = (sysui & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0; if (!isKeyguardShowing) { statusBarTranslucent &= areTranslucentBarsAllowed(); } if (mStatusBar.isVisibleLw() && !statusBarTransient) { //如果 StatusBar 是可见的,且不是暂时显示的,则修改 displayFrames 的mDock、mContent、mVoiceContent、mCurrent,其实就是除去 StatusBar的窗口大小。 final Rect dockFrame = displayFrames.mDock; dockFrame.top = displayFrames.mStable.top; displayFrames.mContent.set(dockFrame); displayFrames.mVoiceContent.set(dockFrame); displayFrames.mCurrent.set(dockFrame); if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format( "dock=%s content=%s cur=%s", dockFrame.toString(), displayFrames.mContent.toString(), displayFrames.mCurrent.toString())); if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent()) { //如果 StatusBar 是不透明的,则修改 mSystem.top 值为 mStable.top 值,这时 Activity 使用 @android:style/Theme.NoTitleBar.Fullscreen 也不会隐藏 StatusBar。 displayFrames.mSystem.top = displayFrames.mStable.top; } } return mStatusBarController.checkHiddenLw(); }}

1)首先设定 of /* overscanFrame /、df / displayFrame /、pf / parentFrame /、vf / visibleFrame */,其中 of、df、pf 设定为 displayFrames.mUnrestricted,即屏幕大小。vf 设定为 displayFrames.mStable,前面有提到mStable 的大小在调用 layoutNavigationBar() 方法后变成了除去NavigationBar的窗口,即应用程序安全显示区域。

2)然后调用 mStatusBar.computeFrameLw() 方法计算 StatusBar 的 mContentFrame 大小。mContentFrame 在 IME (输入法)不存在时与 mDecorFrame 相同,IME 存在时是 mDecorFrame 除去 IME 窗口的大小。

3)然后修改 displayFrames.mStable,即应用的窗口。因为 StatusBar 默认显示在顶部的,所以修改 mStable.top 值。计算 StatusBar 的可显示区域,并将计算结果设置到mStatusBarController中。

4)判断 StatusBar 是否是短暂显示的(Transient)或是半透明的(Translucent)。其中 sysui 就是 mLastSystemUiFlags。 } 如果 StatusBar 是可见的,且不是暂时显示的,则修改 displayFrames 的 mDock、mContent、mVoiceContent、mCurrent,其实就是除去 StatusBar 的窗口大小。还有,如果 StatusBar 是不透明的,则修改 mSystem.top 值为 mStable.top 值,这时 Activity 使用 @android:style/Theme.NoTitleBar.Fullscreen 也不会隐藏 StatusBar。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3